package ftp;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

import org.apache.ftpserver.ftplet.DataConnection;
import org.apache.ftpserver.ftplet.DefaultFtpReply;
import org.apache.ftpserver.ftplet.DefaultFtplet;
import org.apache.ftpserver.ftplet.FtpException;
import org.apache.ftpserver.ftplet.FtpRequest;
import org.apache.ftpserver.ftplet.FtpSession;
import org.apache.ftpserver.ftplet.FtpletResult;

import file.LocalSharedFile;

public class MyFtplet extends DefaultFtplet {
	
	private Map<String, LocalSharedFile> published_file_list;
	
	public MyFtplet() {
		super();
		
		//dictionary, which has index for each file. 
		setFiles(new LinkedHashMap<String, LocalSharedFile>());
		
		loadPublishedFiles();
	}
	
	public static String published_files = "published_files.txt";

	private void loadPublishedFiles() {
		try {
			BufferedReader is = new BufferedReader(new FileReader(published_files));
			String line = is.readLine();
			while(line != null)
			{
				line = line.trim();
				LocalSharedFile file = new LocalSharedFile(line);
				getFiles().put(file.name(), file);			
				
				line = is.readLine();
			}
			
			is.close();
		} catch (FileNotFoundException e) {
			return;
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Add a published file to this FTP server so other peer could get it.
	 * record the item of this file to files map
	 * @param file  a file to publish.
	 */
	public void addFile(LocalSharedFile file)
	{
		getFiles().put(file.name(), file);
		savePublishedFiles();
	}

	private void savePublishedFiles() {
		BufferedOutputStream bytesOut;
		try {
			bytesOut = new BufferedOutputStream(new FileOutputStream(published_files));
			
			Iterator<LocalSharedFile> it = getFiles().values().iterator();
			
			while(it.hasNext())
			{
				LocalSharedFile f = it.next();
				bytesOut.write((f.getPath() + "\n").getBytes());
			}
			
			bytesOut.close();

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	//when FTP server will transfer file to client
    public FtpletResult onDownloadStart(FtpSession session, FtpRequest request) throws FtpException, IOException 
    {
    	LocalSharedFile file = null;
    	
    	//fetch file name from the request and pass it to the file instance
    	String name = request.getArgument();
    	file = getFiles().get(name);
    	
    	if(file == null)
    	{
    		String res = String.format("Cannot find file with signature %s", name);
    		//write to client FTP session
			session.write(new DefaultFtpReply(425, res));
			//SKIP: enumeration final. It indicates that the ftpserver will skip everything.
            return FtpletResult.SKIP;    		
    	}
    	
    	FileInputStream in = new FileInputStream(file.getFile());
		
        // open data connection
        DataConnection out = null;
        session.write(new DefaultFtpReply(150, "Getting data connection."));
        try {
            out = session.getDataConnection().openConnection();
        } catch (Exception ex) {
        }
        if (out == null) {
        	session.write(new DefaultFtpReply(425, "Cannot open data connection."));
            return FtpletResult.SKIP;
        }

		
        // transfer data
        try {
            out.transferToClient(session, in);
            session.write(new DefaultFtpReply(226, "Data transfer okay."));
        } catch (Exception ex) {
        	session.write(new DefaultFtpReply(551, "Data transfer failed."));
        } finally {
            session.getDataConnection().closeDataConnection();
            in.close();
        }
        return FtpletResult.SKIP;
    }

	/**
	 * @param files the files to set
	 */
	void setFiles(Map<String, LocalSharedFile> files) {
		this.published_file_list = files;
	}

	/**
	 * @return the files
	 */
	public Map<String, LocalSharedFile> getFiles() {
		return published_file_list;
	}

	/**
	 * Remove a file from published file list.
	 * 
	 * @param name name should be the file name without path.
	 */
	public void removeFile(String name) {
		published_file_list.remove(name);
		
		savePublishedFiles();
	}
}
